// Copyright (C) 2024 Kumo inc.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//
#pragma once

#include <pthread.h>
#include <sys/socket.h>
#include <kthread/internal/types.h>
#include <kthread/internal/errno.h>

// NOTICE:
//   As the filename implies, this file lists UNSTABLE kthread functions
//   which are likely to be modified or even removed in future release. We
//   don't guarantee any kind of backward compatibility. Don't use these
//   functions if you're not ready to change your code according to newer
//   versions of kthread.

__BEGIN_DECLS

// Schedule tasks created by KTHREAD_NOSIGNAL
extern void kthread_flush();

// Mark the calling kthread as "about to quit". When the kthread is scheduled,
// worker pthreads are not notified.
extern int kthread_about_to_quit();

// Run `on_timer(arg)' at or after real-time `abstime'. Put identifier of the
// timer into *id.
// Return 0 on success, errno otherwise.
extern int kthread_timer_add(kthread_timer_t *id, struct timespec abstime,
                             void (*on_timer)(void *), void *arg);

// Unschedule the timer associated with `id'.
// Returns: 0 - exist & not-run; 1 - still running; EINVAL - not exist.
extern int kthread_timer_del(kthread_timer_t id);

// Suspend caller thread until the file descriptor `fd' has `epoll_events'.
// Returns 0 on success, -1 otherwise and errno is set.
// NOTE: Due to an epoll bug(https://patchwork.kernel.org/patch/1970231),
// current implementation relies on EPOLL_CTL_ADD and EPOLL_CTL_DEL which
// are not scalable, don't use kthread_fd_*wait functions in performance
// critical scenario.
extern int kthread_fd_wait(int fd, unsigned events);

// Suspend caller thread until the file descriptor `fd' has `epoll_events'
// or CLOCK_REALTIME reached `abstime' if abstime is not nullptr.
// Returns 0 on success, -1 otherwise and errno is set.
extern int kthread_fd_timedwait(int fd, unsigned epoll_events,
                                const struct timespec *abstime);

// Close file descriptor `fd' and wake up all threads waiting on it.
// User should call this function instead of close(2) if kthread_fd_wait,
// kthread_fd_timedwait, kthread_connect were called on the file descriptor,
// otherwise waiters will suspend indefinitely and kthread's internal epoll
// may work abnormally after fork() is called.
// NOTE: This function does not wake up pthread waiters.(tested on linux 2.6.32)
extern int kthread_close(int fd);

// Replacement of connect(2) in kthreads.
extern int kthread_connect(int sockfd, const struct sockaddr *serv_addr,
                           socklen_t addrlen);
// Suspend caller thread until connect(2) on `sockfd' succeeds
// or CLOCK_REALTIME reached `abstime' if `abstime' is not nullptr.
extern int kthread_timed_connect(int sockfd, const struct sockaddr *serv_addr,
                                 socklen_t addrlen, const timespec *abstime);

// Add a startup function that each pthread worker will run at the beginning
// To run code at the end, use kutil::thread_atexit()
// Returns 0 on success, error code otherwise.
extern int kthread_set_worker_startfn(void (*start_fn)());

// Add a startup function with tag
extern int kthread_set_tagged_worker_startfn(void (*start_fn)(kthread_tag_t));

// Add a create span function
extern int kthread_set_create_span_func(void *(*func)());

// Stop all kthread and worker pthreads.
// You should avoid calling this function which may cause kthread after main()
// suspend indefinitely.
extern void kthread_stop_world();

// Create a kthread_key_t with an additional arg to destructor.
// Generally the dtor_arg is for passing the creator of data so that we can
// return the data back to the creator in destructor. Without this arg, we
// have to do an extra heap allocation to contain data and its creator.
extern int kthread_key_create2(kthread_key_t *key,
                               void (*destructor)(void *data, const void *dtor_arg),
                               const void *dtor_arg);

// CAUTION: functions marked with [RPC INTERNAL] are NOT supposed to be called
// by RPC users.

// [RPC INTERNAL]
// Create a pool to cache KeyTables so that frequently created/destroyed
// kthreads reuse these tables, namely when a kthread needs a KeyTable,
// it fetches one from the pool instead of creating on heap. When a kthread
// exits, it puts the table back to pool instead of deleting it.
// Returns 0 on success, error code otherwise.
extern int kthread_keytable_pool_init(kthread_keytable_pool_t *);

// [RPC INTERNAL]
// Destroy the pool. All KeyTables inside are destroyed.
// Returns 0 on success, error code otherwise.
extern int kthread_keytable_pool_destroy(kthread_keytable_pool_t *);

// [RPC INTERNAL]
// Put statistics of `pool' into `stat'.
extern int kthread_keytable_pool_getstat(kthread_keytable_pool_t *pool,
                                         kthread_keytable_pool_stat_t *stat);

// [RPC INTERNAL]
// Reserve at most `nfree' keytables with `key' pointing to data created by
// ctor(args).
extern void kthread_keytable_pool_reserve(
        kthread_keytable_pool_t *pool, size_t nfree,
        kthread_key_t key, void *ctor(const void *args), const void *args);

__END_DECLS
